#pragma once

#include <vector>
#include <deque>
#include <thread>
#include <memory>
#include "../corelib/util/allocator.hh"
#include "../corelib/grid/grid.hh"
#include "thirdparty/kconfig/interface/config.h"
#include "../corelib/singleton.hpp"
#include "../corelib/grid/grid.hh"
#include "../corelib/grid//actor/actor.hh"
#include "../corelib/timer_queue.hh"
#include "../corelib/logger/writer.hh"
#include "protocol_analyzer_impl.hh"
#include "module_manager.hh"
#include "command_option.hh"
#include "engine.hh"
#include "component.hh"
#include "component_impl.hh"
#include "../corelib/util/consistent_hashing.hh"

#ifdef KRATOS_COMPILER_LOG_OFF

#define ENGINE_LOG_ERROR(a, b)

#define ENGINE_LOG_EXCEPTION(e, a, b)

#define ENGINE_PARAM_FAIL_RETURN_FALSE(a)\
    return false;

#define ENGINE_PARAM_FAIL_RETURN(a, b)\
    return b;

#else

#define ENGINE_LOG_ERROR(a, b)\
    logwcom("Engine") << a << b << logend;

#define ENGINE_LOG_EXCEPTION(e, a, b)\
    logwcom("Engine") << "Exception:" << e.what() <<","<< a << b << logend;

#define ENGINE_PARAM_FAIL_RETURN_FALSE(a)\
    if (a) {\
        logwcom("Engine") << "[" << __FILE__ << ":" << __LINE__ << "]:" << #a << ", check failed" << logend;\
        return false;\
    }

#define ENGINE_PARAM_FAIL_RETURN(a, b)\
    if (a) {\
        logwcom("Engine") << "[" << __FILE__ << ":" << __LINE__ << "]:" << #a << ", check failed" << logend;\
        return b;\
    }

#endif // KRATOS_COMPILER_LOG_OFF

namespace kratos { namespace engine {

class PathPool;
class ComponentManagerImpl;

// Implements Engine interface
class EngineImpl : public Engine {
private:
    bool run_; // running flag
    kconfig::Config* config_; // configuration
    std::time_t findTimeout_; // the timeout of find-path
    std::time_t now_; // current timestamp in millionseconds
    actor::Actor<grid::Grid>& grid_; // Grid    
    ModuleManager moduleManager_; // module manager
    corelib::TimerQueue timerQueue_; // timer queue
    ProtocolAnalyzerImpl protocolAnalyzer_; // protocol header analyzer
    using PrefindMap = UNORDERED_MAP(SiteType, bool, EngineImpl_prefindMap_);
    PrefindMap prefindMap_; // pre-find path map
    khttp::Http* http_; // khttp::Http instance
    //std::unique_ptr<PathPool> pathPool_; // path pool
    ConsistentHash hashing_; // consistent hashing
    // Entity info.
    struct EntityInfo {
        std::uint64_t entityID; // Entity ID
        PathID pathID; // Path ID
        std::uint32_t entityType; // Entity type
    };
    using LocatorEntityMap = UNORDERED_MAP(std::uint64_t, EntityInfo, EngineImpl_locatorEntityMap_);
    LocatorEntityMap locatorEntityMap_; // {Entity ID, EntityInfo}
    using EntityIDQueue = std::deque<std::uint64_t>;
    using LocatorEntityTypeMap = UNORDERED_MAP(std::uint32_t, EntityIDQueue, EngineImpl_locatorEntityTypeMap_);
    LocatorEntityTypeMap locatorEntityTypeMap_; // { Entity type, Entity ID array }
    using LocatorSiteList = std::list<std::uint64_t>;
    LocatorSiteList locatorSites_; // The site ID list of locator(router)
    using LocatorPathList = std::list<std::uint64_t>;
    LocatorPathList locatorPathes_; // The locator path list
    ComponentManagerImpl componentManager_; // Component manager
    HashType hashType_; // Locator's hash type

    friend class PathPool;

public:
    virtual bool findPath(SiteType siteType, UserData userData) override;
    virtual const grid::SiteID& getPathPeerSiteID(PathID pathID) override;
    virtual void closePath(PathID pathID) override;
    virtual void closeSession(SessionID sessionID) override;
    virtual bool talkPath(PathID pathID, const char* data, std::size_t size) override;
    virtual bool talkSession(SessionID sessionID, const char* data, std::size_t size) override;
    virtual bool talkSite(const grid::SiteID& siteID, const char* data, std::size_t size) override;
    virtual bool broadcastLoactor(const char* data, std::size_t size) override;
    virtual PathID getLocator(std::uint64_t entityID) override;
    virtual PathID getEntityPathID(std::uint64_t entityID) override;
    virtual bool addEntity(PathID pathID, std::uint64_t entityID, std::uint32_t entityType) override;
    virtual std::uint32_t getEntityType(std::uint64_t entityID) override;
    virtual const EntityIDQueue& getEntities(std::uint32_t entityType) override;
    virtual bool removeEntity(PathID pathID, std::uint64_t entityID) override;
    virtual TimerHandle startTimer(std::time_t duration) override;
    virtual TimerHandle startTimerOnce(std::time_t duration) override;
    virtual void cancelTimer(TimerHandle timerID) override;
    virtual ProtocolAnalyzer* getProtocolAnalyzer() override;
    virtual kconfig::Config* getConfig() override;
    virtual kratos::corelib::Log& getLogverb() override;
    virtual kratos::corelib::Log& getLogdebug() override;
    virtual kratos::corelib::Log& getLogwarn() override;
    virtual kratos::corelib::Log& getLogerror() override;
    virtual kratos::corelib::Log& getLogfatal() override;
    virtual const kratos::corelib::Finish& getLogend() override;
    virtual khttp::HttpRequest* newHttpRequest(khttp::HttpCallback* cb, const std::string& host,
        const std::string& uri, int32_t port = 80) override;
    virtual bool newHttpServer(khttp::HttpCallback* cb, const std::string& host, int32_t port = 80) override;
    virtual std::string doCommand(const std::string& moduleName, const std::string& command) override;
    virtual std::string doCommand(const std::string& command) override;
    virtual ObjectPoolManager& getObjectPoolManager() override;
    virtual ComponentManager& getComponentManager() override;
    virtual std::size_t getMemoryPoolSize() override;
    virtual const std::string& getPluginRootPath(const std::string& name) override;
    virtual std::uint64_t getUUID() override;
    virtual std::uint64_t getUUIDRandom() override;
    virtual bool isRouter() override;
    virtual bool connect(const std::string& host, std::uint64_t userData, std::time_t timeout) override;

public:
    // ctor
    EngineImpl();
    // dtor
    virtual ~EngineImpl();
    // start engine
    // @param configFilePath config file path
    bool start(const std::string& configFilePath = "");
    // start as a root engine
    bool startAsRoot();
    // stop engine
    bool stop();
    // runs as a daemon
    // @retval true
    // @retval false fail
    bool startAsDaemon();
    // loop for debug mode
    void debug_loop(std::time_t now);
    // returns module manager
    ModuleManager& getModuleManager();

private:
    // Analyzes configuration
    // @retval true
    // @retval false fail
    bool parseConfig();
    // Registers callbacks
    // @retval true
    // @retval false fail
    bool registerCallbacks();
    // Starts logger
    // @retval true
    // @retval false fail
    bool startLogger();
    // Load & start module
    // @retval true
    // @retval false fail
    bool startModules();
    // Runs timers' callback
    void processTimer();
    // pre-find path
    void prefind();
    // main loop
    void main_loop();
    // start core LIB
    bool start_corelib(bool noModule = false);
    // stop for debug mode
    void stopAll();
    // find path
    bool internalFindPath(SiteType siteType, UserData userData);
    // find path
    bool internalFindPath(const grid::SiteID& siteID, UserData userData);
    // Remove the entity path
    void removeEntityPath(PathID pathID);
    // Start core
    // @param noModule Donot load any module
    bool startCore(bool noModule);
};

}}
